31.Python:文件读写

IO操作与读写文件

读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。不论哪种,一定要注意编码和解码的一致性,否则会出现乱码或报错。

打开文件

python中文件的读取封装后,使用时非常简单,读取文件调用open()函数,返回一个文件对象。例如:
my_test_file = open("io_test.txt", 'r')

  • 打开文件:open()函数

open()函数详细解释:
open()函数返回一个文件对象,最常用的有两个参数:open(filename, mode)

第一个参数是包含文件名的字符串。
第二个参数是另一个字符串,其中包含一些描述文件使用方式的字符。 模式可以是'r'仅读取文件时,'w' 仅写入(将删除同名的现有文件),并 'a'打开文件进行追加; 写入文件的任何数据都会自动添加到最后。 'r+'打开文件进行读写。所述模式参数是可选的; 'r'将被假设,如果它被省略。

通常,文件以文本模式打开,这意味着从文件读取和写入文件,这些文件以特定编码进行编码。如果未指定编码,则默认值取决于平台。'b'附加到模式后以二进制模式打开文件 :现在以bytes对象的形式读取和写入数据。此模式应用于所有不包含文本的文件。如下表所示:

不同模式打开文件

在文本模式下,读取时的默认设置是将平台特定的行结尾(\n在Unix上,\r\n在Windows上)转换为just \n。在文本模式下写入时,默认设置是将事件的发生转换\n为特定于平台的行结尾。这幕后的修改文件数据精细的文本文件,但会喜欢,在破坏了二进制数据 JPEGEXE文件。在读取和写入此类文件时要非常小心地使用二进制模式。

# 打开文件:第一种写法
try:
    my_test_file = open("io_test.txt", 'r')
    # content = my_test_file.read()
    # print(content)
finally:
    if my_test_file:
        my_test_file.close()

# 打开文件:第二种写法
with open('io_test.txt', 'r') as f:
    # print('f:', f.read() + '\t \t')
    lines = f.readlines()

    for index, line in enumerate(lines):
        print('第{0}行:{1}'.format(index, line))


# 以二进制方式打开图片
with open('2.jpg', 'r') as pic:
    print( pic.read())

with在处理文件对象时,最好使用关键字。优点是文件在套件完成后正确关闭,即使在某个时刻引发了异常。使用with也比写相当于短得多try...finally块:

如果您没有使用该with关键字,那么您应该调用 f.close()以关闭该文件并立即释放它使用的任何系统资源。如果您没有显式关闭文件,Python的垃圾收集器最终将销毁该对象并为您关闭打开的文件,但该文件可能会保持打开状态一段时间。另一个风险是不同的Python实现将在不同的时间进行清理。

通过with语句或通过调用关闭文件对象后f.close(),再尝试使用该文件对象将自动失败。会报错:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

文件对象的方法

打开文件后,就要进行读写操作了。本小节中的下面的示例将假定f是已创建一个名为f的文件对象 ,即有:
with open('io_test.txt', 'r') as f:

  • f.read(size)读取文件的内容

要读取文件的内容,请调用f.read(size),读取一些数据并将其作为字符串(在文本模式下)或字节对象(在二进制模式下)返回。 size是可选的数字参数。当省略不传size或为负数时,将读取并返回文件的全部内容; 如果文件的大小是机器内存的两倍,那么这就是你的问题。否则,最多读取并返回大小字节。如果已到达文件末尾,f.read()则返回空字符串('')。
io_test.txt文件中的内容是:

2018年世界杯半决赛全部结束,7月15日23:00法国和克罗地亚将会师决赛竞逐大力神杯,7月14日22:00比利时与英格兰队争夺第三名。

例1:当省略不传size或为负数时,将读取并返回文件的全部内容:

with open("io_test.txt", encoding='utf-8') as f:
    content_str = f.read()
    print(content_str)

或者:

with open("io_test.txt", encoding='utf-8') as f:
    content_str = f.read(-1)
    print(content_str)

运行结果都是:

2018年世界杯半决赛全部结束,7月15日23:00法国和克罗地亚将会师决赛竞逐大力神杯,7月14日22:00比利时与英格兰队争夺第三名。
  • f.readline()读取一行内容

readline()函数详细解释:
readline()函数返回一个从文件中读取一行内容,可以省略参数或者传入一个参数:readline(limit)
limit参数表示:最大限制可读取limit个字符。如果实际读取到的内容字符数小于等于传入的limit的值,则按照实际读入的返回,否则就只返回limit个字符(且此时下一行内容按照后面未读取到的内容开始算起)。

f.readline()从文件中读取一行; 换行符\n留在字符串的末尾,如果文件没有以换行符结尾,则只在文件的最后一行省略。这使得返回值明确无误。
如果f.readline()返回一个空字符串,则表示已到达文件末尾;如果f.readline()返回一个\n只包含一个换行符的字符串,则表示当前行是空行。

再次提示:
f.readline()读到空行时,返回结果是:\n;当f.readline()读到文件末尾时,返回结果是:空字符串''

项目中有文件io_test.txt内容如下:

2018年俄罗斯世界杯是第21届世界杯足球赛。
2022年卡塔尔世界杯是第22届世界杯足球赛。

readline()函数不传入参数时,编写测试代码:

with open('io_test.txt', encoding='utf-8') as f:
    s1 = f.readline()
    print(s1)

    s2 = f.readline()
    print(s2)

运行结果:

2018年俄罗斯世界杯是第21届世界杯足球赛。

2022年卡塔尔世界杯是第22届世界杯足球赛。

readline()函数传入参数limit时,编写测试代码:

with open('io_test.txt', encoding='utf-8') as f:
    s1 = f.readline(4)
    print(s1)

    s2 = f.readline()
    print(s2)
    
    s3 = f.readline()
    print(s3)

运行结果如下:

2018
年俄罗斯世界杯是第21届世界杯足球赛。

2022年卡塔尔世界杯是第22届世界杯足球赛。

即:如果实际读取到的内容字符数小于等于传入的limit的值,则按照实际读入的返回,否则就只返回limit个字符(且此时下一行内容按照后面未读取到的内容开始算起)。

  • f.readlines()函数:读取文件中的所有行

如果要读取文件的所有行,使用 f.readlines(),返回该文件中包含的所有行。
如果设置可选参数 sizehint, 则读取指定长度的字节, 并且将这些字节按行分割。。

# readlines 
with open("io_test.txt", encoding="utf-8") as f:
    lines = f.readlines()
    for line in lines:
        print(line)

运行结果:

2018年俄罗斯世界杯是第21届世界杯足球赛。

2022年卡塔尔世界杯是第22届世界杯足球赛。

另一种方式是迭代一个文件对象然后读取每行:

with open("io_test.txt", encoding="utf-8") as f:
    for line in f:
        print(line,end='')

运行结果:

2018年俄罗斯世界杯是第21届世界杯足球赛。
2022年卡塔尔世界杯是第22届世界杯足球赛。

提示:这个方法很简单, 但是并没有提供一个很好的控制。 因为两者的处理机制不同, 最好不要混用。

  • f.write()函数:将 string 写入到文件中, 然后返回写入的字符数。

f.write(string) 将 string 写入到文件中, 然后返回写入的字符数。

写入文件,这次采用的权限模式是: w表示:打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。

运行前:文件目录

运行前,文件目录中是没有file_write.txt文件的,如上图。

# f.write()
with open('file_write.txt','w+', encoding="utf-8") as f:
    count = f.write("Python is an elegant program language")
    print(count)

运行结果:(影响3个)
1.文件目录的变化:创建出了file_write.txt文件。

运行后:文件目录

2.在新创建出的file_write.txt文件中写入了内容:Python is an elegant program language,如下图:

file_write.txt```文件中的内容

3.打印结果:

37

返回此次写入的字符数。

  • 如果要写入一些不是字符串的东西,如元组、字典, 那么将需要先进行转换。
    例如:
# 写入元组
with open("file_write_tuple.txt", 'w', encoding='utf-8') as f:
    t = (2018, '二0一八年')
    count = f.write(str(t))
    print(count)

运行结果:(影响3个)
1.1.文件目录的变化:创建出了file_write_tuple.txt文件。


2.在新创建出的file_write_tuple.txt文件中写入了内容:(2018, '二0一八年')
3.打印结果:

15
  • f.tell()函数

f.tell()函数,返回文件对象当前指针所处的位置, 它是从文件开头开始算起的字节数。

  • f.seek(offset, from_what) 函数,

如果要改变文件当前的位置, 可以使用 f.seek(offset, from_what) 函数。
from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,例如:
seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
seek(x,1) : 表示从当前位置往后移动x个字符
seek(-x,2):表示从文件的结尾往前移动x个字符
from_what 值可以省略,默认为0,即文件开头。下面给出一个完整的例子:

# seek()
with open("file_write_bit.txt", 'wb+') as f:
    f.write(b'0123456789abcedfgh')

    s_3 = f.seek(3)
    print(s_3)
    print(f.tell())
    print(f.read(1))

    print('--------------')

    s_4 = f.seek(4, 1)
    print(s_4)
    print(f.tell())
    print(f.read(1))

运行结果:

3
3
b'3'
--------------
8
8
b'8'
  • f.close()函数

在文本文件中 (那些打开文件的模式下没有 b 的), 只会相对于文件起始位置进行定位。
当你处理完一个文件后, 调用 f.close()来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常。ValueError: I/O operation on closed file.

# 读取文件:第一种写法
try:
    my_test_file = open("io_test.txt", encoding='utf-8')
    content = my_test_file.read()
    print(content)
finally:
    if my_test_file:
        my_test_file.close()

当处理一个文件对象时, 使用 with 关键字是非常好的方式。在结束后, 它会帮你正确的关闭文件。 而且写起来也比 try - finally 语句块要简短。

# 读取文件:第二种写法
with open('io_test.txt', 'r') as f:

文件对象还有其他方法, 如 isatty() 和 trucate(), 但这些通常比较少用。

pickle 模块

python的pickle模块实现了基本的数据序列和反序列化。
通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。
通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
基本接口:
pickle.dump(obj, file, [,protocol])
有了 pickle 这个对象, 就能对 file 以读取的形式打开:
x = pickle.load(file)
注解:从 file 中读取一个字符串,并将它重构为原来的python对象。
file: 类文件对象,有read()和readline()接口。

以Json数据结构保存数据

我们已经可以轻松地将字符串写入文件并从文件中读取。数字需要更多的努力,因为该read()方法只返回字符串,必须将其传递给类似的函数int(),它接受类似字符串'123' 并返回其数值123.当您想要保存更复杂的数据类型(如嵌套列表和字典,手工解析和序列化变得复杂)。

Python允许使用称为Json的流行数据交换格式,而不是让用户不断编写和调试代码,以将复杂的数据类型保存到文件中。调用的标准模块json,可以采用Python数据层次结构,并将它们转换为字符串表示形式,这个过程称为序列化。从字符串表示中重建数据称为反序列化。在序列化和反序列化之间,表示对象的字符串可能已存储在文件或数据中,或通过网络连接发送到某个远程服务器。

  • 提示

JSON格式通常被现代应用程序用于允许数据交换的首要选择。

  • 序列化

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

  • 反序列化

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。Python提供了pickle模块来实现序列化。

Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON。

如果您有一个对象x,则可以使用一行简单的代码查看其JSON字符串表示:

import json
# 把对象转换为Json字符串

t = [2018, "21届世界杯", '俄罗斯']

# 核心代码,使用前要先导入json模块,此处是json.dumps(t)方法,区别于方法json.dump(t,f)
json_str = json.dumps(t)

print(t)
print(json_str)

运行结果:

[2018, '21届世界杯', '俄罗斯']
[2018, "21\u5c4a\u4e16\u754c\u676f", "\u4fc4\u7f57\u65af"]

提示:
1.使用前要先导入json模块,此处是json.dumps(t)方法,区别于方法json.dump(t,f)。如果你遇到了下面错误,请检查方法是否使用错误:

Traceback (most recent call last):
  File "F:/python_projects/io_file/my_file.py", line 84, in <module>
    json_str = json.dump(t)
TypeError: dump() missing 1 required positional argument: 'fp'

此文中用的python是3.5,在版本3.6中已更改:所有可选参数现在仅为关键字。
json.dumps(obj,*,skipkeys = False,ensure_ascii = True,check_circular = True,allow_nan = True,cls = None,indent = None,separators = None,default = None,sort_keys = False,** kw )

2.json.dumps(t)方法的另一个变体json.dump(t,f)```,只是将对象序列化为文本文件。
dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。

import json
# 直接把对象以Json字符串写入文件
t = [2018, "21届世界杯", '俄罗斯']
with open('file_write_json.txt','w') as f:
    json_str = json.dump(t,f)

要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化。

  • loads()反序列化
import json
# 直接把对象以Json字符串写入文件
t = [2018, "21届世界杯", '俄罗斯']
with open('file_write_json.txt','w') as f:
    json_str = json.dumps(t)
    print(json_str)
    
    # 反序列化
    t2 = json.loads(json_str)
    print(t2)

运行结果:

[2018, "21\u5c4a\u4e16\u754c\u676f", "\u4fc4\u7f57\u65af"]
[2018, '21届世界杯', '俄罗斯']
  • load()反序列化

load()反序列化,直接将文件反序列化为内存中的对象。

import json
t = [2018, "21届世界杯", '俄罗斯']
with open('file_write_json.txt', 'r+') as f:
    t3 = json.load(f)
    print(t3)

运行结果:

[2018, '21届世界杯', '俄罗斯']

pickle

与JSON相反,pickle是一种允许对任意复杂Python对象进行序列化的协议。因此,它特定于Python,不能用于与其他语言编写的应用程序通信。默认情况下它也是不安全的:如果数据是由熟练的攻击者精心设计的,则反序列化来自不受信任来源的pickle数据可以执行任意代码。

说白了就是:Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容。

接下来一节会单独拿出来学习序列化、反序列化。

小结

本文着重学习文件读写和涉及到了序列化和反序列化,接下来一节会单独拿出来学习序列化、反序列化。


更多了解,可关注公众号:人人懂编程


微信公众号:人人懂编程
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,313评论 0 5
  • pyton review 学习指南 https://www.zhihu.com/question/29138020...
    孙小二wuk阅读 1,015评论 0 2
  • 暗淡的天色 没有一丝生气 所幸 月亮升起 她的妖娆 她的妩媚 都这么令人着迷 她彻夜倾倒出她所积攒的能量 给这大地...
    MOON七阅读 128评论 0 0
  • 忙了一天,累并充实。 新工作给了我更多的赚钱动力和对生活的热爱。 今天,发生了一些和平时一样的好事情,却让我心里再...
    丁小颖阅读 183评论 0 0
  • 再次打开这个废弃已久的账号,最新的文章发布日期定格在2017年2月7日,距今过了整整一年两个月还要多出个零头。 如...
    五格纸阅读 341评论 0 1